Solutions/TenableIO/Data Connectors/exports_store.py (136 lines of code) (raw):

import logging from enum import Enum from azure.data.tables import TableClient, UpdateMode from azure.core.exceptions import ResourceNotFoundError, ResourceExistsError, HttpResponseError from .tenable_helper import TenableStatus class ExportsTableStore: def __init__(self, connection_string, table_name): self.connection_string = connection_string self.table_name = table_name def create(self): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: try: table_client.create_table() except ResourceExistsError: logging.warn("Table already exists") def post(self, pk: str, rk: str, data: dict = None): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: entity_template = { 'PartitionKey': pk, 'RowKey': rk, } if data is not None: entity_template.update(data) try: table_client.create_entity(entity_template) except Exception as e: logging.warn('could not post entity to table') logging.warn(e) raise e def get(self, pk: str, rk: str): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: try: logging.info( f'looking for {pk} - {rk} on table {self.table_name}') return table_client.get_entity(pk, rk) except ResourceNotFoundError: return None def upsert(self, pk: str, rk: str, data: dict = None): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: logging.info(f'upserting {pk} - {rk} on table {self.table_name}') entity_template = { 'PartitionKey': pk, 'RowKey': rk, } if data is not None: entity_template.update(data) return table_client.upsert_entity(mode=UpdateMode.REPLACE, entity=entity_template) def update_if_found(self, pk: str, rk: str, data: dict = None): if self.get(pk, rk) is not None: self.merge(pk, rk, data) def query_by_partition_key(self, pk): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = {u"key": pk} name_filter = u"PartitionKey eq @key" try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] # def get_chunk_details_by_job_id(self, pk): # total_chunk_count = 0 # failed_chunk_count = 0 # finished_chunk_count = 0 # failed_chunk_ids = [] # finished_chunk_ids = [] # for chunk in self.query_by_partition_key(pk): # total_chunk_count += 1 # if 'jobStatus' in chunk and chunk['jobStatus'] == 'FINISH': # finished_chunk_count += 1 # return total_chunk_count def query_for_finished_chunks_by_partition_key(self, pk): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = {'key': pk, 'status': TenableStatus.finished.value} name_filter = 'PartitionKey eq @key and jobStatus eq @status' try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def query_for_all_finished_chunks(self): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = {'status': TenableStatus.finished.value} name_filter = 'jobStatus eq @status' try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def query_for_failed_chunks_by_partition_key(self, pk): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = {'key': pk, 'status': TenableStatus.failed.value} name_filter = 'PartitionKey eq @key and jobStatus eq @status' try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def query_for_all_failed_chunks(self): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = {'status': TenableStatus.failed.value} name_filter = 'jobStatus eq @status' try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def query_for_all_processing_chunks(self): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = { 'failedStatus': TenableStatus.failed.value, 'processingStatus': TenableStatus.processing.value, 'sentStatus': TenableStatus.sent_to_queue.value, 'sendingStatus': TenableStatus.sending_to_queue.value } name_filter = 'jobStatus eq @failedStatus or jobStatus eq @processingStatus or jobStatus eq @sentStatus or jobStatus eq @sendingStatus' try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def batch(self, operations): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: return table_client.submit_transaction(operations=operations) def list_all(self): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) return table_client.list_entities() def merge(self, pk: str, rk: str, data: dict = None): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: logging.info(f'upserting {pk} - {rk} on table {self.table_name}') entity_template = { 'PartitionKey': pk, 'RowKey': rk, } if data is not None: entity_template.update(data) return table_client.upsert_entity(mode=UpdateMode.MERGE, entity=entity_template) class ExportsTableNames(Enum): TenableExportStatsTable = "TenableExportStatsTable" TenableAssetExportTable = "TenableAssetExportTable" TenableVulnExportTable = "TenableVulnExportTable"